package com.gkzf.ai.module.crm.util;

import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
public class ExpressionEvaluator {
    /**
     * 评估由数字和运算符组成的表达式列表。
     * 遵循运算符优先级：先乘除，后加减。
     * 输入列表格式应为 [数字, 运算符, 数字, 运算符, 数字, ...]
     * 使用 BigDecimal 进行精确计算。
     *
     * @param expressionParts 表达式的组成部分列表，例如 ["1000.0", "+", "8000.0", "*", "3.5"]
     * @return 表达式计算结果 (BigDecimal)
     * @throws IllegalArgumentException 如果表达式无效（例如结构错误，包含非数字/非运算符的字符串，除数不能为零）
     */
    public static BigDecimal evaluate(List<String> expressionParts) {
        if (expressionParts == null || expressionParts.isEmpty()) {
            throw new IllegalArgumentException("表达式列表不能为空。");
        }

        // 创建一个可修改的列表副本
        List<String> expression = new ArrayList<>(expressionParts);

        // 定义高优先级运算符
        List<String> highPrecedenceOps = Arrays.asList("*", "/");
        // 定义低优先级运算符
        List<String> lowPrecedenceOps = Arrays.asList("+", "-");
        // 所有支持的运算符，用于校验
        List<String> allOperators = new ArrayList<>();
        allOperators.addAll(highPrecedenceOps);
        allOperators.addAll(lowPrecedenceOps);


        // --- 第一趟：处理乘法和除法 ---
        // 循环查找并计算乘除，直到列表中不再包含乘除运算符
        while (containsAny(expression, highPrecedenceOps)) {
            // 找到第一个出现的乘号或除号的索引
            int opIndex = findFirstOccurrence(expression, highPrecedenceOps);

            // 检查操作符前后是否有操作数
            if (opIndex <= 0 || opIndex >= expression.size() - 1) {
                throw new IllegalArgumentException("表达式结构错误: 运算符 '" + expression.get(opIndex) + "' 位置无效。");
            }

            String operand1Str = expression.get(opIndex - 1);
            String operand2Str = expression.get(opIndex + 1);

            BigDecimal operand1;
            BigDecimal operand2;
            try {
                operand1 = new BigDecimal(operand1Str);
                operand2 = new BigDecimal(operand2Str);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("操作数必须是有效的数字: '" + operand1Str + "' 或 '" + operand2Str + "' 在运算符 '" + expression.get(opIndex) + "' 附近", e);
            }

            BigDecimal result;
            String operator = expression.get(opIndex);
            result = switch (operator) {
                case "*" -> operand1.multiply(operand2);
                case "/" -> {
                    if (operand2.compareTo(BigDecimal.ZERO) == 0) {
                        yield BigDecimal.ZERO;
                    }
                    // 使用适当的精度进行除法运算，避免精度问题
                    // 示例：保留10位小数，四舍五入。根据你的实际需求调整精度和舍入模式
                    yield operand1.divide(operand2, 10, RoundingMode.HALF_UP);
                }
                default ->
                    // 理论上不会到达这里，因为 while 条件和 findFirstOccurrence 限制了操作符
                        throw new IllegalStateException("内部错误：第一趟扫描处理了非乘除运算符：" + operator);
            };

            // 将计算结果替换掉原来的三个元素（操作数 1, 运算符, 操作数 2）
            // 从右往左移除，避免索引问题
            expression.remove(opIndex + 1); // 移除第二个操作数
            expression.remove(opIndex);     // 移除运算符
            expression.remove(opIndex - 1); // 移除第一个操作数
            expression.add(opIndex - 1, result.toPlainString()); // 在原第一个操作数的位置插入结果的字符串形式
        }

        // --- 第二趟：处理加法和减法 ---
        // 循环查找并计算加减，直到列表中不再包含加减运算符
        while (containsAny(expression, lowPrecedenceOps)) {
            // 找到第一个出现的加号或减号的索引
            int opIndex = findFirstOccurrence(expression, lowPrecedenceOps);

            // 检查操作符前后是否有操作数
            if (opIndex <= 0 || opIndex >= expression.size() - 1) {
                throw new IllegalArgumentException("表达式结构错误: 运算符 '" + expression.get(opIndex) + "' 位置无效。");
            }

            String operand1Str = expression.get(opIndex - 1);
            String operand2Str = expression.get(opIndex + 1);

            BigDecimal operand1;
            BigDecimal operand2;
            try {
                operand1 = new BigDecimal(operand1Str);
                operand2 = new BigDecimal(operand2Str);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("操作数必须是有效的数字: '" + operand1Str + "' 或 '" + operand2Str + "' 在运算符 '" + expression.get(opIndex) + "' 附近", e);
            }


            BigDecimal result;
            String operator = expression.get(opIndex);
            switch (operator) {
                case "+":
                    result = operand1.add(operand2);
                    break;
                case "-":
                    result = operand1.subtract(operand2);
                    break;
                default:
                    // 理论上不会到达这里
                    throw new IllegalStateException("内部错误：第二趟扫描处理了非加减运算符：" + operator);
            }

            // 将计算结果替换掉原来的三个元素（操作数 1, 运算符, 操作数 2）
            expression.remove(opIndex + 1); // 移除第二个操作数
            expression.remove(opIndex);     // 移除运算符
            expression.remove(opIndex - 1); // 移除第一个操作数
            expression.add(opIndex - 1, result.toPlainString()); // 在原第一个操作数的位置插入结果的字符串形式
        }

        // --- 最终结果 ---
        if (expression.size() != 1) {
            // 如果表达式列表最终不只剩一个元素，说明表达式结构有问题
            // 可能原因：连续两个操作符，连续两个操作数，非数字/非操作符的元素未被处理等
            // 在实际应用中，可以在开始前或每一步后增加更严格的结构检查
            throw new IllegalArgumentException("表达式计算后剩余多个元素，可能表达式结构有误或包含非法字符: " + expression);
        }

        // 最终列表中唯一的元素就是结果的字符串表示
        try {
            return new BigDecimal(expression.get(0));
        } catch (NumberFormatException e) {
            // 如果最终元素不是数字，也说明有问题
            throw new IllegalArgumentException("表达式计算结果无效 (非数字): '" + expression.get(0) + "'", e);
        }
    }

    /**
     * 检查列表中是否包含任何指定的运算符。
     */
    private static boolean containsAny(List<String> list, List<String> ops) {
        for (String op : ops) {
            if (list.contains(op)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 查找列表中第一个出现的指定运算符的索引。
     * 如果列表包含多个指定的运算符，返回索引最小（最左边）的那个。
     */
    private static int findFirstOccurrence(List<String> list, List<String> ops) {
        int minIndex = -1;
        for (String op : ops) {
            int index = list.indexOf(op);
            if (index != -1) {
                if (minIndex == -1 || index < minIndex) {
                    minIndex = index;
                }
            }
        }
        return minIndex; // 如果列表中不包含任何指定运算符，返回 -1
    }

    // --- 示例用法 ---
    public static void main(String[] args) {
        // 示例表达式: 1000.0 + 8000.0 * 3.5  (先算 8000.0 * 3.5)
        List<String> expression1 = Arrays.asList("10.0", "+", "80.0", "*", "3.5");

        // 示例表达式: 10 + 5 * 2 / 5 - 1 (先算 5 * 2, 再算 10 / 5, 最后加减)
        // 10 + 10 / 5 - 1
        // 10 + 2.0 - 1
        // 12.0 - 1
        // 11.0
        List<String> expression2 = Arrays.asList("10", "*", "5", "+", "20", "/", "5", "-", "1");


        try {
            System.out.println("--- 示例 1 ---");
            System.out.println("表达式: " + String.join(" ", expression1));
            BigDecimal result1 = ExpressionEvaluator.evaluate(expression1);
            System.out.println("计算结果: " + result1); // 预期 29000.0

            System.out.println("\n--- 示例 2 ---");
            System.out.println("表达式: " + String.join(" ", expression2));
            BigDecimal result2 = ExpressionEvaluator.evaluate(expression2);
            System.out.println("计算结果: " + result2); // 预期 11.0


            System.out.println("\n--- 错误例子 (非数字或运算符) ---");
            List<String> expression3 = Arrays.asList("10", "+", "abc");
            try {
                ExpressionEvaluator.evaluate(expression3);
            } catch (IllegalArgumentException e) {
                System.out.println("捕获到预期错误: " + e.getMessage());
            }

            System.out.println("\n--- 错误例子 (结构错误 - 运算符开头) ---");
            List<String> expression4 = Arrays.asList("+", "10");
            try {
                ExpressionEvaluator.evaluate(expression4);
            } catch (IllegalArgumentException e) {
                System.out.println("捕获到预期错误: " + e.getMessage());
            }

            System.out.println("\n--- 错误例子 (除数为零) ---");
            List<String> expression5 = Arrays.asList("10", "/", "0");
            try {
                ExpressionEvaluator.evaluate(expression5);
            } catch (IllegalArgumentException e) {
                System.out.println("捕获到预期错误: " + e.getMessage());
            }


        } catch (IllegalArgumentException e) {
            System.err.println("表达式评估失败: " + e.getMessage());
        }
    }
}
